package com.minimalist.lindi.manager;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.http.HttpStatus;
import com.minimalist.common.config.exception.BusinessException;
import com.minimalist.common.entity.basic.enums.FileEnum;
import com.minimalist.common.entity.basic.vo.file.FileUploadRespVO;
import com.minimalist.common.entity.basic.vo.file.FileVO;
import com.minimalist.common.entity.lindi.enums.LindiPurchaseApplicationEnum;
import com.minimalist.common.entity.lindi.enums.LindiPurchaseApplicationFileTypeEnum;
import com.minimalist.common.entity.lindi.po.*;
import com.minimalist.common.entity.lindi.vo.enterpriseaccount.LindiEnterpriseAccountVO;
import com.minimalist.common.entity.lindi.vo.excel.ProjectItemData;
import com.minimalist.common.entity.lindi.vo.purchaseapplication.LindiPurchaseApplicationApprovalVO;
import com.minimalist.common.entity.lindi.vo.purchaseapplication.LindiPurchaseApplicationVO;
import com.minimalist.common.entity.lindi.vo.purchaseapplicationfile.LindiPurchaseApplicationFileVO;
import com.minimalist.common.entity.lindi.vo.purchaseapplicationprojectitem.LindiPurchaseApplicationProjectItemVO;
import com.minimalist.common.entity.lindi.vo.purchaseapplicationtownitem.LindiPurchaseApplicationTownItemVO;
import com.minimalist.common.feign.client.basic.FileFeignClient;
import com.minimalist.common.feign.encoder.ByteArrayMultipartFile;
import com.minimalist.common.response.R;
import com.minimalist.common.utils.ExcelGenerate;
import com.minimalist.common.utils.UnqIdUtil;
import com.minimalist.lindi.mapper.*;
import com.minimalist.lindi.service.LindiPurchaseApplicationProjectItemService;
import com.minimalist.lindi.service.LindiPurchaseApplicationTownItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 林地指标交易平台_指标购买申请管理器
 *
 * @author 11987
 */
@Component
public class LindiPurchaseApplicationManager{

    @Autowired
    private LindiPurchaseApplicationFileMapper lindiPurchaseApplicationFileMapper;

    @Autowired
    private LindiPurchaseApplicationMapper lindiPurchaseApplicationMapper;

    @Autowired
    private LindiEnterpriseAccountMapper lindiEnterpriseAccountMapper;

    @Autowired
    private FileFeignClient fileFeignClient;

    @Autowired
    private LindiPurchaseApplicationTownItemService lindiPurchaseApplicationTownItemService;

    @Autowired
    private LindiPurchaseApplicationProjectItemService lindiPurchaseApplicationProjectItemService;

    @Autowired
    private LindiPurchaseApplicationProjectItemMapper lindiPurchaseApplicationProjectItemMapper;

    @Autowired
    private LindiPurchaseApplicationTownItemMapper lindiPurchaseApplicationTownItemMapper;

    /**
     * 根据企业ID获取企业账户信息
     *
     * @param enterpriseId 企业ID
     * @return 企业账户信息
     */
    public LindiEnterpriseAccountVO getEnterpriseAccountByEnterpriseId(Long enterpriseId){
        LindiEnterpriseAccount lindiEnterpriseAccount = lindiEnterpriseAccountMapper
                .selectEnterpriseAccountByEnterpriseAccountId(enterpriseId);
        Assert.notNull(lindiEnterpriseAccount, () -> new BusinessException("企业账户不存在"));
        return BeanUtil.copyProperties(lindiEnterpriseAccount, LindiEnterpriseAccountVO.class);
    }

    /**
     * 根据购买申请ID和文件类型获取文件列表
     *
     * @param purchaseApplicationId 购买申请ID
     * @param fileType              文件类型
     * @return 文件列表
     */
    public List<LindiPurchaseApplicationFileVO> getFileListByPurchaseApplicationIdAndFileType(
            Long purchaseApplicationId, String fileType){
        List<LindiPurchaseApplicationFile> lindiPurchaseApplicationFiles =
                lindiPurchaseApplicationFileMapper
                        .selectFileListByPurchaseApplicationIdAndFileType(
                                purchaseApplicationId, fileType);

        if (lindiPurchaseApplicationFiles.isEmpty()){
            return List.of();
        }

        List<Long> fileIdList = lindiPurchaseApplicationFiles.stream()
                .map(LindiPurchaseApplicationFile::getFileId).toList();

        R<List<FileVO>> filesResult = fileFeignClient.getFileListByFileIds(fileIdList);
        Assert.isTrue(filesResult.getCode() == HttpStatus.HTTP_OK
                        && filesResult.getData() != null
                , () -> new BusinessException("获取文件信息失败"));

        List<FileVO> fileVOList = filesResult.getData();

        // 将LindiPurchaseApplicationFile转换为LindiPurchaseApplicationFileVO并设置文件信息
        return lindiPurchaseApplicationFiles.stream()
                .map(purchaseFile -> {
                    LindiPurchaseApplicationFileVO vo = BeanUtil.copyProperties(
                            purchaseFile, LindiPurchaseApplicationFileVO.class);

                    // 设置文件信息
                    fileVOList.stream()
                            .filter(fileVO -> fileVO.getFileId().equals(purchaseFile.getFileId()))
                            .findFirst()
                            .ifPresent(vo::setFile);

                    return vo;
                })
                .collect(Collectors.toList());
    }

    /**
     * 审批购买申请
     *
     * @param approvalVO 审批信息
     */
    @Transactional(rollbackFor = Exception.class)
    public void approvePurchaseApplication(
            LindiPurchaseApplicationApprovalVO approvalVO){
        // 查询购买申请信息
        LindiPurchaseApplication purchaseApplication = lindiPurchaseApplicationMapper
                .selectPurchaseApplicationByPurchaseApplicationId(approvalVO.getPurchaseApplicationId());
        Assert.notNull(purchaseApplication, () -> new BusinessException("购买申请不存在"));

        String currentStatus = purchaseApplication.getPurchaseApplicationStatus();

        // 根据当前状态和审批结果处理不同的业务逻辑
        if (LindiPurchaseApplicationEnum.StatusEnum.PENDING_APPROVAL.getCode().toString().equals(currentStatus)){
            // 当前状态：待审批
            if (approvalVO.getApprovalResult() == 1){
                // 审批通过：待审批 → 待确认，需要填写镇街信息
                if (approvalVO.getTownItemList() == null || approvalVO.getTownItemList().isEmpty()){
                    throw new BusinessException("审批通过时必须填写镇街信息");
                }
                purchaseApplication.setPurchaseApplicationStatus(
                        LindiPurchaseApplicationEnum
                                .StatusEnum.APPROVED.getCode().toString());
            } else if (approvalVO.getApprovalResult() == 2){
                // 审批不通过：待审批 → 已驳回
                purchaseApplication.setPurchaseApplicationStatus(
                        LindiPurchaseApplicationEnum.StatusEnum.REJECTED.getCode()
                                .toString());
            } else{
                throw new BusinessException("无效的审批结果");
            }
        } else if (LindiPurchaseApplicationEnum.StatusEnum.APPROVED.getCode()
                .toString().equals(currentStatus)){
            // 当前状态：待确认
            if (approvalVO.getApprovalResult() == 1){
                // 确认通过：待确认 → 已完成，需要上传确认函
                if (approvalVO.getConfirmationFileId() == null){
                    throw new BusinessException("确认时必须上传确认函");
                }
                purchaseApplication.setPurchaseApplicationStatus(LindiPurchaseApplicationEnum
                        .StatusEnum.Completed.getCode().toString());
            } else{
                throw new BusinessException("待确认状态只能进行确认操作");
            }
        } else{
            throw new BusinessException("当前状态不允许进行审批操作");
        }

        // 更新购买申请信息
        purchaseApplication.setUpdateId(approvalVO.getUpdateId());
        purchaseApplication.setUpdateTime(approvalVO.getUpdateTime());

        int updateResult = lindiPurchaseApplicationMapper.update(purchaseApplication);
        Assert.isTrue(updateResult == 1, () -> new BusinessException("更新购买申请状态失败"));

        // 根据状态处理不同的业务逻辑
        if (LindiPurchaseApplicationEnum.StatusEnum.PENDING_APPROVAL.getCode().toString().equals(currentStatus)
                && approvalVO.getApprovalResult() == 1){
            // 待审批 → 待确认：保存镇街信息并生成确认函模板
            if (approvalVO.getTownItemList() != null && !approvalVO.getTownItemList().isEmpty()){
                // 设置购买申请ID
                approvalVO.getTownItemList().forEach(townItem -> {
                    townItem.setPurchaseApplicationId(approvalVO.getPurchaseApplicationId());
                    townItem.setTownItemId(UnqIdUtil.uniqueId());
                });
                // 批量保存镇街指标数据
                lindiPurchaseApplicationTownItemService.batchAddTownItems(
                        approvalVO.getTownItemList());
            }
            // 生成确认函模板并上传
            generateConfirmationTemplate(approvalVO.getPurchaseApplicationId(), approvalVO);
        } else if (LindiPurchaseApplicationEnum.StatusEnum.APPROVED.getCode().toString().equals(currentStatus)
                && approvalVO.getApprovalResult() == 1){
            // 待确认 → 已完成：保存确认函文件关联
            if (approvalVO.getConfirmationFileId() != null){
                LindiPurchaseApplicationFile confirmationFile = new LindiPurchaseApplicationFile();
                confirmationFile.setPurchaseApplicationId(approvalVO.getPurchaseApplicationId());
                confirmationFile.setFileId(approvalVO.getConfirmationFileId());
                confirmationFile.setFileType(LindiPurchaseApplicationFileTypeEnum
                        .CONFIRMATION_LETTER.getCode());
                confirmationFile.setCreateId(approvalVO.getUpdateId());
                confirmationFile.setCreateTime(approvalVO.getUpdateTime());
                confirmationFile.setUpdateId(approvalVO.getUpdateId());
                confirmationFile.setUpdateTime(approvalVO.getUpdateTime());
                int insertResult = lindiPurchaseApplicationFileMapper.insert(confirmationFile);
                Assert.isTrue(insertResult == 1, () -> new BusinessException("保存确认函文件关联失败"));
            }
        }

        // TODO: 可以在这里添加审批记录的保存逻辑
        // 例如保存到审批历史表中
    }

    /**
     * 根据购买申请ID查询完整的购买申请信息（包含项目信息和镇街购买信息）
     *
     * @param purchaseApplicationId 购买申请ID
     * @return 完整的购买申请信息
     */
    public LindiPurchaseApplicationVO getPurchaseApplicationWithDetails(Long purchaseApplicationId){
        // 查询购买申请基本信息
        LindiPurchaseApplication purchaseApplication = lindiPurchaseApplicationMapper
                .selectPurchaseApplicationByPurchaseApplicationId(purchaseApplicationId);
        Assert.notNull(purchaseApplication, () -> new BusinessException("购买申请不存在"));

        // 转换为VO
        LindiPurchaseApplicationVO vo = BeanUtil.copyProperties(purchaseApplication, LindiPurchaseApplicationVO.class);

        // 查询并设置项目指标列表
        List<LindiPurchaseApplicationProjectItemVO> projectItemList = getProjectItemsByPurchaseApplicationId(purchaseApplicationId);
        vo.setProjectItemList(projectItemList);

        // 查询并设置镇街指标列表
        List<LindiPurchaseApplicationTownItemVO> townItemList = getTownItemsByPurchaseApplicationId(purchaseApplicationId);
        vo.setTownItemList(townItemList);

        // 查询并设置文件列表
        List<LindiPurchaseApplicationFileVO> fileList =
                getFileListByPurchaseApplicationIdAndFileType(purchaseApplicationId, null);
        vo.setFileList(fileList);

        return vo;
    }

    /**
     * 根据购买申请ID查询项目信息
     *
     * @param purchaseApplicationId 购买申请ID
     * @return 项目指标列表
     */
    public List<LindiPurchaseApplicationProjectItemVO> getProjectItemsByPurchaseApplicationId(Long purchaseApplicationId){
        List<LindiPurchaseApplicationProjectItem> projectItems = lindiPurchaseApplicationProjectItemMapper
                .selectProjectItemListByPurchaseApplicationId(purchaseApplicationId);
        return projectItems.stream()
                .map(item -> BeanUtil.copyProperties(item, LindiPurchaseApplicationProjectItemVO.class))
                .collect(Collectors.toList());
    }

    /**
     * 根据购买申请ID查询镇街购买信息
     *
     * @param purchaseApplicationId 购买申请ID
     * @return 镇街指标列表
     */
    public List<LindiPurchaseApplicationTownItemVO> getTownItemsByPurchaseApplicationId(Long purchaseApplicationId){
        List<LindiPurchaseApplicationTownItem> townItems = lindiPurchaseApplicationTownItemMapper
                .selectTownItemListByPurchaseApplicationId(purchaseApplicationId);
        return townItems.stream()
                .map(item -> BeanUtil.copyProperties(item, LindiPurchaseApplicationTownItemVO.class))
                .collect(Collectors.toList());
    }


    /**
     * 构建审核意见
     *
     * @param townItems 镇街指标列表
     * @return 审核意见
     */
    private String buildApprovalOpinion(List<LindiPurchaseApplicationTownItem> townItems){
        if (townItems == null || townItems.isEmpty()){
            return "经审核，同意购买林地指标。\n\n\n主要领导签字（公章）：                日期：       年         月       日";
        }

        StringBuilder opinion = new StringBuilder("经审核，同意由晋农公司分别向");
        BigDecimal totalIndex = BigDecimal.ZERO;

        for (int i = 0; i < townItems.size(); i++){
            LindiPurchaseApplicationTownItem townItem = townItems.get(i);
            BigDecimal townIndex = new BigDecimal(townItem.getTownIndex());
            totalIndex = totalIndex.add(townIndex);

            opinion.append(townItem.getTownName())
                    .append("购买林地指标")
                    .append(townIndex.stripTrailingZeros().toPlainString())
                    .append("亩");

            if (i < townItems.size() - 1){
                opinion.append("，");
            }
        }

        opinion.append("，合计")
                .append(totalIndex.stripTrailingZeros().toPlainString())
                .append("亩。\n\n\n主要领导签字（公章）：                日期：       年         月       日");

        return opinion.toString();
    }

    /**
     * 使用ExcelGenerate工具类生成Excel文件（当模板文件不存在时的备用方案）
     *
     * @param outputStream 输出流
     * @param fillData     填充数据
     */
    private void generateExcelWithTemplate(ByteArrayOutputStream outputStream, Map<String, Object> fillData){
        try{
            @SuppressWarnings("unchecked")
            List<Map<String, Object>> projectList = (List<Map<String, Object>>) fillData.get("projectList");

            // 创建项目列表数据
            List<ProjectItemData> projectItemList = projectList.stream()
                    .map(project -> {
                        ProjectItemData data = new ProjectItemData();
                        data.setProjectName((String) project.get("projectName"));
                        data.setProjectCode((String) project.get("projectCode"));
                        data.setProjectIndex((String) project.get("projectIndex"));
                        return data;
                    })
                    .collect(Collectors.toList());

            // 没有模板文件时，直接写入Excel
            ExcelGenerate.writeExcel(outputStream, "购买申请确认函", ProjectItemData.class, projectItemList);

        } catch (Exception e){
            throw new BusinessException("生成Excel文件失败: " + e.getMessage());
        }
    }

    /**
     * 生成确认函模板并上传
     *
     * @param purchaseApplicationId 购买申请ID
     * @param approvalVO            审批信息
     */
    private void generateConfirmationTemplate(Long purchaseApplicationId, LindiPurchaseApplicationApprovalVO approvalVO){
        try{
            // 1. 查询购买申请信息
            LindiPurchaseApplication purchaseApplication = lindiPurchaseApplicationMapper
                    .selectPurchaseApplicationByPurchaseApplicationId(purchaseApplicationId);
            Assert.notNull(purchaseApplication, () -> new BusinessException("购买申请不存在"));

            // 2. 查询项目指标信息
            List<LindiPurchaseApplicationProjectItem> projectItems = lindiPurchaseApplicationProjectItemMapper
                    .selectProjectItemListByPurchaseApplicationId(purchaseApplicationId);

            // 3. 查询镇街指标信息
            List<LindiPurchaseApplicationTownItem> townItems = lindiPurchaseApplicationTownItemMapper
                    .selectTownItemListByPurchaseApplicationId(purchaseApplicationId);

            // 4. 构建审核意见
            String approvalOpinion = buildApprovalOpinion(townItems);

            // 5. 准备Excel填充数据
            Map<String, Object> fillData = new HashMap<>();

            // 计算项目指标总计
            BigDecimal totalProjectIndex = projectItems.stream()
                    .map(project -> new BigDecimal(project.getProjectIndex()))
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            // 项目列表数据
            List<Map<String, Object>> projectList = projectItems.stream().map(project -> {
                Map<String, Object> projectData = new HashMap<>();
                projectData.put("projectName", project.getProjectName());
                projectData.put("projectCode", project.getProjectCode());
                projectData.put("projectIndex", project.getProjectIndex());
                return projectData;
            }).collect(Collectors.toList());

            fillData.put("projectList", projectList);
            // 添加项目指标总计
            fillData.put("totalProjectIndex", totalProjectIndex.stripTrailingZeros().toPlainString());
            // 添加审核意见（单项）
            fillData.put("approvalOpinion", approvalOpinion);

            // 6. 生成Excel文件
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

            // 准备填充数据 - 根据EasyExcel模板填充规范
            // 单个变量使用 {变量名} 格式，列表变量使用 {list.字段名} 格式
            Map<String, Object> fillDataMap = new HashMap<>();
            // 添加单个数据字段
            fillDataMap.put("totalProjectIndex", fillData.get("totalProjectIndex"));
            fillDataMap.put("approvalOpinion", fillData.get("approvalOpinion"));
            // 准备列表数据 - 注意：EasyExcel要求列表数据直接作为对象列表传递
            @SuppressWarnings("unchecked")
            List<Map<String, Object>> projectDataList = (List<Map<String, Object>>) fillData.get("projectList");

            // 使用Spring Boot推荐的资源加载方式
            try{
                // 使用ClassPathResource加载模板文件
                ClassPathResource resource = new ClassPathResource("lindi_purchase_confirm_template.xlsx");

                // 详细的资源检查和调试信息
                System.out.println("=== Excel模板文件调试信息 ===");
                System.out.println("资源路径: " + resource.getPath());
                System.out.println("资源是否存在: " + resource.exists());
                System.out.println("资源是否可读: " + resource.isReadable());

                try{
                    System.out.println("资源URI: " + resource.getURI());
                    System.out.println("资源文件大小: " + resource.contentLength() + " bytes");
                } catch (Exception e){
                    System.out.println("获取资源URI或大小失败: " + e.getMessage());
                }

                // 检查资源是否存在且可读
                if (!resource.exists()){
                    throw new BusinessException("Excel模板文件不存在: lindi_purchase_confirm_template.xlsx");
                }

                if (!resource.isReadable()){
                    throw new BusinessException("Excel模板文件不可读: lindi_purchase_confirm_template.xlsx");
                }

                // 使用try-with-resources确保资源正确关闭
                try (InputStream templateInputStream = resource.getInputStream()){
                    // 验证输入流是否有效
                    if (templateInputStream == null){
                        throw new BusinessException("无法获取模板文件输入流");
                    }

                    // 检查输入流是否有数据
                    if (templateInputStream.available() == 0){
                        throw new BusinessException("模板文件输入流为空");
                    }

                    System.out.println("模板文件输入流可用字节数: " + templateInputStream.available());

                    // 验证文件格式 - 检查文件头部是否为有效的Excel格式
                    if (!templateInputStream.markSupported()){
                        throw new BusinessException("模板文件输入流不支持标记，无法进行格式验证");
                    }

                    templateInputStream.mark(8); // 标记前8个字节
                    byte[] header = new byte[8];
                    int bytesRead = templateInputStream.read(header);
                    templateInputStream.reset(); // 重置到标记位置

                    if (bytesRead < 4){
                        throw new BusinessException("模板文件格式无效：文件头部数据不足");
                    }

                    // 检查ZIP文件头部（Excel文件本质上是ZIP文件）
                    // ZIP文件头部：50 4B 03 04 或 50 4B 05 06 或 50 4B 07 08
                    if (!(header[0] == 0x50 && header[1] == 0x4B &&
                            (header[2] == 0x03 || header[2] == 0x05 || header[2] == 0x07))){
                        StringBuilder hexString = new StringBuilder();
                        for (int i = 0; i < Math.min(bytesRead, 8); i++){
                            hexString.append(String.format("%02X ", header[i]));
                        }
                        throw new BusinessException("模板文件格式无效：不是有效的Excel文件。文件头部: " + hexString.toString().trim() +
                                ". 这通常是由于Maven资源过滤导致的文件损坏，请检查pom.xml配置");
                    }

                    System.out.println("✓ 模板文件格式验证通过");

                    System.out.println("开始使用ExcelGenerate填充模板...");
                    // 使用ExcelGenerate工具类填充模板
                    // 模板中占位符格式：
                    // - 单个数据：{totalProjectIndex}, {approvalOpinion}
                    // - 列表数据：{list.projectName}, {list.projectCode}, {list.projectIndex}
                    // 注意：ExcelGenerate使用FillWrapper("list", listData)，所以列表占位符必须以"list."开头
                    ExcelGenerate.fillComplexTemplate(templateInputStream, outputStream, fillDataMap, projectDataList);
                    System.out.println("Excel模板填充完成");
                }

            } catch (BusinessException e){
                // 重新抛出业务异常
                throw e;
            } catch (Exception templateException){
                System.out.println("ClassPathResource加载模板失败，尝试备用方案: " + templateException.getMessage());

                // 备用方案1：尝试从文件系统直接读取
                boolean fallbackSuccess = false;
                try{
                    String templatePath = "d:\\projects\\zhixin\\minimalist-saas\\minimalist-lindi\\src\\main\\resources\\lindi_purchase_confirm_template.xlsx";
                    java.io.File templateFile = new java.io.File(templatePath);

                    if (templateFile.exists() && templateFile.canRead()){
                        System.out.println("找到文件系统中的模板文件: " + templatePath);

                        try (InputStream fileInputStream = new java.io.FileInputStream(templateFile)){
                            // 验证文件格式
                            byte[] header = new byte[4];
                            int bytesRead = fileInputStream.read(header);

                            if (bytesRead >= 4 && header[0] == 0x50 && header[1] == 0x4B){
                                System.out.println("文件系统模板文件格式验证通过");

                                // 重新创建输入流进行处理
                                try (InputStream processInputStream = new java.io.FileInputStream(templateFile)){
                                    ExcelGenerate.fillComplexTemplate(processInputStream, outputStream, fillDataMap, projectDataList);
                                    fallbackSuccess = true;
                                    System.out.println("使用文件系统模板文件成功生成Excel");
                                }
                            } else{
                                System.out.println("文件系统模板文件格式无效");
                            }
                        }
                    } else{
                        System.out.println("文件系统中未找到模板文件: " + templatePath);
                    }
                } catch (Exception fileSystemException){
                    System.out.println("文件系统备用方案失败: " + fileSystemException.getMessage());
                }

                // 备用方案2：生成简单Excel文件
                if (!fallbackSuccess){
                    try{
                        System.out.println("使用简单Excel生成方案");
                        generateExcelWithTemplate(outputStream, fillData);
                    } catch (Exception fallbackException){
                        throw new BusinessException("生成确认函模板失败: 模板处理错误 - " + templateException.getMessage() +
                                "; 所有备用方案都失败 - " + fallbackException.getMessage() +
                                ". 请检查Maven配置中的资源过滤设置，确保Excel文件不被过滤损坏");
                    }
                }
            }

            // 7. 创建MultipartFile并上传
            String fileName = "购买申请确认函_" + purchaseApplication.getPurchaseApplicationCode() + "_" +
                    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + ".xlsx";

            MultipartFile multipartFile = new ByteArrayMultipartFile(
                    "file",
                    fileName,
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                    outputStream.toByteArray()
            );

            // 8. 上传文件
            R<FileUploadRespVO> uploadResult = fileFeignClient
                    .uploadFile(multipartFile
                            , FileEnum.FileSource
                                    .LINDI_PURCHASE_APPLICATION_CONFIRM_TEMPLATE
                                    .getCode()
                            , approvalVO.getUpdateId(), approvalVO.getUpdateId()
                            , approvalVO.getTenantId());
            Assert.isTrue(uploadResult.getCode() == HttpStatus.HTTP_OK && uploadResult.getData() != null,
                    () -> new BusinessException("确认函模板上传失败"));

            // 9. 保存文件关联关系
            LindiPurchaseApplicationFile confirmationFile = new LindiPurchaseApplicationFile();
            confirmationFile.setPurchaseApplicationId(purchaseApplicationId);
            confirmationFile.setFileId(uploadResult.getData().getFileId());
            confirmationFile.setFileType(LindiPurchaseApplicationFileTypeEnum.CONFIRMATION_TEMPLATE.getCode());
            confirmationFile.setCreateId(approvalVO.getUpdateId());
            confirmationFile.setCreateTime(approvalVO.getUpdateTime());
            confirmationFile.setUpdateId(approvalVO.getUpdateId());
            confirmationFile.setUpdateTime(approvalVO.getUpdateTime());

            int insertResult = lindiPurchaseApplicationFileMapper.insert(confirmationFile);
            Assert.isTrue(insertResult == 1, () -> new BusinessException("保存确认函模板文件关联失败"));

        } catch (Exception e){
            throw new BusinessException("生成确认函模板失败: " + e.getMessage());
        }
    }

}